package org.jiuling.basics.converter;


import org.jiuling.basics.enums.DateTimeFormatEnum;
import org.jiuling.basics.enums.DateTypeEnum;
import org.jiuling.basics.exception.BasicsException;
import org.jiuling.basics.info.ChineseCalendarInfo;
import org.jiuling.basics.validation.Assertions;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 时间转换
 * @author 紫灵
 * @date 2021年09月27日 16时56分16秒
 */
public class DateConvert {

    /** 私有构造函数，禁止创建对象 */
    private DateConvert(){}

    /**
     * 转16进制时间
     * @param date 时间
     * @param dateTimeFormatEnum 时间格式枚举
     * @return 16进制时间
     */
    public static String toHexDate(Date date, DateTimeFormatEnum dateTimeFormatEnum){
        String[] dateArray = dateToString(date,dateTimeFormatEnum).replaceAll("(.{2})", "$1 ").split(" ");
        StringBuffer dataStringBuffer = new StringBuffer();
        for (String s : dateArray) {
            dataStringBuffer.append(IntegerConvert.toHexString(Integer.parseInt(s),1));
        }
        return dataStringBuffer.toString();
    }

    /**
     * 字符串转换为日期
     * @param dateString 日期字符串
     * @param pattern 日期格式
     * @return 日期
     */
    public static Date stringToDate(String dateString, String pattern) throws BasicsException {
        try {
            return new SimpleDateFormat(pattern).parse(dateString);
        } catch (ParseException e) {
            throw new BasicsException("时间格式转换异常！");
        }
    }

    /**
     * 字符串转换为日期
     * @param dateString 日期字符串
     * @param dateTimeFormatEnum 时间格式转换枚举
     * @return 日期
     */
    public static Date stringToDate(String dateString, DateTimeFormatEnum dateTimeFormatEnum) throws BasicsException {
        return stringToDate(dateString,dateTimeFormatEnum.getFormat());
    }

    /**
     * 日期转字符串
     * @param date 日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    public static String dateToString(Date date,String pattern){
        return new SimpleDateFormat(pattern).format(date);
    }

    /**
     * 日期转字符串
     * @param date 日期
     * @param dateTimeFormatEnum 时间格式转换枚举
     * @return 日期字符串
     */
    public static String dateToString(Date date, DateTimeFormatEnum dateTimeFormatEnum){
        return dateToString(date,dateTimeFormatEnum.getFormat());
    }

    /**
     * 修改时间
     * @param date 基础时间
     * @param year 年
     * @param month 月
     * @param day 日
     * @param hour 时
     * @param minute 分
     * @param second 秒
     * @return 修改后的时间
     */
    public static Date modificationDate(Date date,int year,int month,int day,int hour,int minute,int second){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        //修改年
        calendar.add(Calendar.YEAR,year);
        //修改月
        calendar.add(Calendar.MONTH,month);
        //修改日
        calendar.add(Calendar.DATE,day);
        //修改时
        calendar.add(Calendar.HOUR,hour);
        //修改分
        calendar.add(Calendar.MINUTE,minute);
        //修改秒
        calendar.add(Calendar.SECOND,second);
        return calendar.getTime();
    }

    /**
     * 修改时间
     * @param date 基础时间
     * @param dateType 日期类型
     * @param duration 时长
     * @return 修改后的时间
     */
    public static Date modificationDate(Date date, DateTypeEnum dateType, int duration){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        switch (dateType){
            // 修改年
            case YEAR -> calendar.add(Calendar.YEAR,duration);
            // 修改月
            case MONTH -> calendar.add(Calendar.MONTH,duration);
            // 修改日
            case DAY -> calendar.add(Calendar.DATE,duration);
            // 修改时
            case HOUR -> calendar.add(Calendar.HOUR,duration);
            // 修改分
            case MINUTY -> calendar.add(Calendar.MINUTE,duration);
            // 修改秒
            case SECOND -> calendar.add(Calendar.SECOND,duration);
        }
        return calendar.getTime();
    }


    /**
     * 修改天数
     * @param date 基础时间
     * @param day 修改的天数
     * @return 修改后的时间
     */
    public static Date modifyDay(Date date,int day){
        return modificationDate(date,0,0,day,0,0,0);
    }

    /**
     * 修改年数
     * @param date 基础时间
     * @param year 修改的年数
     * @return 修改后的时间
     */
    public static Date modifyYear(Date date,int year) {
        return modificationDate(date,year,0,0,0,0,0);
    }

    /**
     * 修改月数
     * @param date 基础时间
     * @param month 修改的月数
     * @return 修改后的时间
     */
    public static Date modifyMonth(Date date,int month){
        return modificationDate(date,0,month,0,0,0,0);
    }

    /**
     * 修改小时数
     * @param date 基础时间
     * @param hour 修改的小时数
     * @return 修改后的时间
     */
    public static Date modifyHour(Date date,int hour) {
        return modificationDate(date,0,0,0,hour,0,0);
    }

    /**
     * 修改分钟数
     * @param date 基础时间
     * @param minute 修改的分钟数
     * @return 修改后的时间
     */
    public static Date modifyMinute(Date date,int minute){
        return modificationDate(date,0,0,0,0,minute,0);
    }

    /**
     * 修改秒数
     * @param date 基础时间
     * @param second 修改的秒数
     * @return 修改后的时间
     */
    public static Date modifySecond(Date date,int second){
        return modificationDate(date,0,0,0,0,0,second);
    }

    /**
     * 计算农历月份天数
     * @param year 年份
     * @param month 月份
     * @return 返回对应月份总天数
     */
    private static int calculateLunarMonthDayNumber(int year, int month)  {

        if ((ChineseCalendarInfo.LUNAR_INFO[year - ChineseCalendarInfo.MIN_YEAR] & (0x100000 >> month)) == 0) {
            return 29;
        } else {
            return 30;
        }
    }

    /**
     * 获取农历年总天数
     * @param year 年份
     * @return 农历年对应总天数
     */
    public static int getLunarYearDayNumber(int year) {
        lunarYearIsEligible(year);
        int i, sum = 348;
        if (getLunarLeapMonth(year) != 0) {
            sum = 377;
        }
        int monthInfo = ChineseCalendarInfo.LUNAR_INFO[year - ChineseCalendarInfo.MIN_YEAR] & 0x0FFF80;
        for (i = 0x80000; i > 0x7; i >>= 1) {
            if ((monthInfo & i) != 0) {
                sum += 1;
            }
        }
        return sum;
    }

    /**
     * 获取农历闰月
     * @param year 年份
     * @return 闰月 0则表示没有闰月
     */
    public static int getLunarLeapMonth(int year)  {
        lunarYearIsEligible(year);
        return ((ChineseCalendarInfo.LUNAR_INFO[year - ChineseCalendarInfo.MIN_YEAR] & 0xF00000)) >> 20;
    }

    /**
     * 获取农历月份天数
     * @param year 年份
     * @param month 月份
     * @return 返回对应月份总天数
     */
    public static int getLunarMonthDayNumber(int year, int month) {
        lunarYearIsEligible(year);
        lunarMonthIsEligible(month);
        // 获取润月
        int leapMonth = getLunarLeapMonth(year);
        int offset = 0;
        // 如果本年有闰月且传入月份大于闰月时，需要校正
        if (leapMonth != 0 && month > leapMonth) {
            offset = 1;
        }
        if(leapMonth != 0 && leapMonth == month){
            return calculateLunarMonthDayNumber(year, month + 1);
        }else {
            return calculateLunarMonthDayNumber(year, month + offset);
        }
    }

    /**
     * 农历年符合条件
     * @param year 年份
     */
    private static void lunarYearIsEligible(int year){
        Assertions.isTrue("农历年不符合规范！",ChineseCalendarInfo.MIN_YEAR < year && year < ChineseCalendarInfo.MAX_YEAR);
    }

    /**
     * 农历月符合条件
     * @param month 月份
     */
    private static void lunarMonthIsEligible(int month){
        Assertions.isTrue("农历月不符合规范！",month > 0 && month < 13);
    }

}
